import json
import time
import hashlib
import hmac
import base64
import sqlite3
from typing import Dict, List, Optional, Any, Set
from fastapi import FastAPI, HTTPException, Query, WebSocket, WebSocketDisconnect, Depends
from fastapi.middleware.cors import CORSMiddleware
from paho.mqtt import client as mqtt_client
from pydantic import BaseModel
import logging
from datetime import datetime
import asyncio
from contextlib import asynccontextmanager

# 导入自定义模块
from api import router as api_router
from repository import (
    MQTTRepository,
    LicensePlateRepository,
    SensorRepository,
    ParkingSpotRepository
)
from service import VehicleService, ParkingService

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Alibaba Cloud IoT MQTT configuration
PRODUCT_KEY = "k1i7vknfFR7"
DEVICE_NAME = "android_app"
DEVICE_SECRET = "255838622d37467a7137334c9d935bfe"
MQTT_HOST = "iot-06z00e2kwdh0ger.mqtt.iothub.aliyuncs.com"
MQTT_PORT = 1883

# Topic to subscribe to
TOPIC = "/sys/k1i7vknfFR7/android_app/thing/service/property/set"

# In-memory data storage
license_plate_data = {}
infrared_sensor_data = {
    "sensor1": False,
    "sensor2": False,
    "sensor3": False,
    "sensor4": False
}
# Store raw messages for debugging
raw_messages = []

# SQLite database setup
DB_PATH = "mqtt_data.db"

def init_db():
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    
    # Create tables if they don't exist
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS mqtt_messages (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        topic TEXT NOT NULL,
        payload TEXT NOT NULL,
        timestamp INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS license_plates (
        id TEXT PRIMARY KEY,
        plate_number TEXT NOT NULL,
        status TEXT,
        timestamp INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS sensors (
        id TEXT PRIMARY KEY,
        value INTEGER NOT NULL,
        timestamp INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS parking_spots (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        spot_number TEXT UNIQUE NOT NULL,
        status TEXT NOT NULL DEFAULT 'free',
        sensor_id TEXT,
        last_updated INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS reservations (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        plate_number TEXT NOT NULL,
        spot_number TEXT NOT NULL,
        start_time INTEGER NOT NULL,
        end_time INTEGER,
        status TEXT NOT NULL,
        created_at INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS vehicle_records (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        plate_number TEXT NOT NULL,
        spot_number TEXT NOT NULL,
        entry_time INTEGER NOT NULL,
        exit_time INTEGER,
        status TEXT NOT NULL,
        created_at INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS income_records (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        reservation_id INTEGER,
        plate_number TEXT NOT NULL,
        amount REAL NOT NULL,
        start_time INTEGER NOT NULL,
        end_time INTEGER NOT NULL,
        created_at INTEGER NOT NULL
    )
    ''')
    
    cursor.execute('''
    CREATE TABLE IF NOT EXISTS pricing_rules (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        free_duration INTEGER NOT NULL,
        unit_duration INTEGER NOT NULL,
        unit_price REAL NOT NULL,
        is_active INTEGER NOT NULL DEFAULT 0,
        created_at INTEGER NOT NULL
    )
    ''')
    
    # 检查是否需要初始化一些基本数据
    cursor.execute("SELECT COUNT(*) FROM parking_spots")
    if cursor.fetchone()[0] == 0:
        # 初始化一些车位
        spots = [
            ("A001", "free", "sensor1", int(time.time() * 1000)),
            ("A002", "free", "sensor2", int(time.time() * 1000)),
            ("A003", "free", "sensor3", int(time.time() * 1000)),
            ("A004", "free", "sensor4", int(time.time() * 1000)),
            ("B001", "free", None, int(time.time() * 1000)),
            ("B002", "free", None, int(time.time() * 1000)),
            ("B003", "free", None, int(time.time() * 1000)),
            ("B004", "free", None, int(time.time() * 1000)),
        ]
        cursor.executemany(
            "INSERT INTO parking_spots (spot_number, status, sensor_id, last_updated) VALUES (?, ?, ?, ?)",
            spots
        )
        
    # 检查是否需要初始化计费规则
    cursor.execute("SELECT COUNT(*) FROM pricing_rules")
    if cursor.fetchone()[0] == 0:
        # 初始化默认计费规则
        timestamp = int(time.time() * 1000)
        cursor.execute(
            """
            INSERT INTO pricing_rules 
            (name, free_duration, unit_duration, unit_price, is_active, created_at) 
            VALUES (?, ?, ?, ?, ?, ?)
            """,
            ("默认规则", 300, 3600, 5.0, 1, timestamp)
        )
    
    conn.commit()
    conn.close()
    logger.info("Database initialized")

# WebSocket connection manager
class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        if websocket in self.active_connections:
            self.active_connections.remove(websocket)

    async def broadcast(self, message: dict):
        for connection in self.active_connections:
            try:
                await connection.send_json(message)
            except Exception as e:
                logger.error(f"Error sending WebSocket message: {e}")

manager = ConnectionManager()

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Initialize database
    init_db()
    yield
    # Cleanup (if needed)

app = FastAPI(title="Parking System API", lifespan=lifespan)

# 添加CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有源，生产环境应该限制
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 集成API路由
app.include_router(api_router)

# API Models
class SensorData(BaseModel):
    id: str
    value: bool
    timestamp: Optional[int] = None

class LicensePlateData(BaseModel):
    id: str
    plate_number: str
    timestamp: Optional[int] = None
    status: Optional[str] = None
    
class MQTTMessage(BaseModel):
    topic: str
    payload: Dict[str, Any]
    timestamp: int

# Database functions
def save_mqtt_message(topic: str, payload: dict, timestamp: int):
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute(
            "INSERT INTO mqtt_messages (topic, payload, timestamp) VALUES (?, ?, ?)",
            (topic, json.dumps(payload), timestamp)
        )
        conn.commit()
        conn.close()
    except Exception as e:
        logger.error(f"Error saving MQTT message to database: {e}")

def save_license_plate(plate_id: str, plate_number: str, status: str, timestamp: int):
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute(
            "INSERT OR REPLACE INTO license_plates (id, plate_number, status, timestamp) VALUES (?, ?, ?, ?)",
            (plate_id, plate_number, status, timestamp)
        )
        conn.commit()
        conn.close()
    except Exception as e:
        logger.error(f"Error saving license plate to database: {e}")

def save_sensor_data(sensor_id: str, value: bool, timestamp: int):
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute(
            "INSERT OR REPLACE INTO sensors (id, value, timestamp) VALUES (?, ?, ?)",
            (sensor_id, 1 if value else 0, timestamp)
        )
        conn.commit()
        conn.close()
    except Exception as e:
        logger.error(f"Error saving sensor data to database: {e}")

def get_mqtt_messages(limit: int = 10):
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute(
            "SELECT topic, payload, timestamp FROM mqtt_messages ORDER BY timestamp DESC LIMIT ?",
            (limit,)
        )
        rows = cursor.fetchall()
        conn.close()
        
        messages = []
        for row in rows:
            messages.append({
                "topic": row[0],
                "payload": json.loads(row[1]),
                "timestamp": row[2]
            })
        return messages
    except Exception as e:
        logger.error(f"Error retrieving MQTT messages from database: {e}")
        return []

def get_license_plates():
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute("SELECT id, plate_number, status, timestamp FROM license_plates")
        rows = cursor.fetchall()
        conn.close()
        
        plates = {}
        for row in rows:
            plates[row[0]] = {
                "id": row[0],
                "plate_number": row[1],
                "status": row[2],
                "timestamp": row[3]
            }
        return plates
    except Exception as e:
        logger.error(f"Error retrieving license plates from database: {e}")
        return {}

def get_sensors():
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute("SELECT id, value, timestamp FROM sensors")
        rows = cursor.fetchall()
        conn.close()
        
        sensors = {}
        for row in rows:
            sensors[row[0]] = bool(row[1])
        return sensors
    except Exception as e:
        logger.error(f"Error retrieving sensors from database: {e}")
        return {}

# MQTT Client setup
def generate_mqtt_credentials():
    """Generate MQTT connection parameters using Alibaba Cloud IoT method"""
    timestamp = str(int(time.time() * 1000))
    client_id = f"{PRODUCT_KEY}.{DEVICE_NAME}|securemode=2,signmethod=hmacsha256,timestamp={timestamp}|"
    username = f"{DEVICE_NAME}&{PRODUCT_KEY}"
    
    # Generate password
    content = f"clientId{PRODUCT_KEY}.{DEVICE_NAME}deviceName{DEVICE_NAME}productKey{PRODUCT_KEY}timestamp{timestamp}"
    password = hmac.new(
        DEVICE_SECRET.encode(), 
        content.encode(), 
        digestmod=hashlib.sha256
    ).hexdigest()
    
    return {
        "client_id": client_id,
        "username": username,
        "password": password
    }

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        logger.info("Connected to MQTT Broker!")
        # Subscribe to the topic
        client.subscribe(TOPIC)
        logger.info(f"Subscribed to topic: {TOPIC}")
    else:
        logger.error(f"Failed to connect, return code {rc}")

async def broadcast_message(message_data):
    await manager.broadcast(message_data)

def on_message(client, userdata, msg):
    try:
        payload = json.loads(msg.payload.decode())
        logger.info(f"Received message on topic {msg.topic}: {payload}")
        
        timestamp = int(time.time() * 1000)
        
        # Store raw message for debugging
        message_data = {
            "topic": msg.topic,
            "payload": payload,
            "timestamp": timestamp
        }
        
        raw_messages.append(message_data)
        
        # Keep only the last 100 messages in memory
        if len(raw_messages) > 100:
            raw_messages.pop(0)
        
        # Save to database
        MQTTRepository.save_mqtt_message(msg.topic, payload, timestamp)
        
        # Process the message based on your data format
        if "params" in payload:
            params = payload["params"]
            
            # Extract license plate data if available
            if "license_plate" in params:
                license_plate = params["license_plate"]
                if isinstance(license_plate, dict) and "id" in license_plate and "plate_number" in license_plate:
                    license_plate["timestamp"] = timestamp
                    license_plate_data[license_plate["id"]] = license_plate
                    logger.info(f"Updated license plate data: {license_plate}")
                    
                    # Save to database
                    LicensePlateRepository.save_license_plate(
                        license_plate["id"],
                        license_plate["plate_number"],
                        license_plate.get("status", "unknown"),
                        timestamp
                    )
                    
                    # 处理车辆进出
                    plate_number = license_plate["plate_number"]
                    status = license_plate.get("status")
                    
                    if status == "in":
                        # 车辆进入，默认使用第一个车位
                        spot_number = "A001"
                        result = VehicleService.process_vehicle_entry(plate_number, spot_number)
                        logger.info(f"Vehicle entry processed: {result}")
                    elif status == "out":
                        # 车辆离开
                        result = VehicleService.process_vehicle_exit(plate_number)
                        logger.info(f"Vehicle exit processed: {result}")
            
            # Extract infrared sensor data if available
            if "infrared_sensor" in params:
                sensor_data = params["infrared_sensor"]
                if isinstance(sensor_data, dict):
                    for sensor_id, value in sensor_data.items():
                        if sensor_id in infrared_sensor_data:
                            infrared_sensor_data[sensor_id] = bool(value)
                            logger.info(f"Updated sensor {sensor_id} to {value}")
                            
                            # Save to database
                            SensorRepository.save_sensor_data(sensor_id, bool(value), timestamp)
                            
                            # 更新车位状态
                            # 查找与传感器关联的车位
                            spots = ParkingSpotRepository.get_parking_spots()
                            for spot in spots:
                                if spot["sensor_id"] == sensor_id:
                                    # 更新车位状态
                                    ParkingService.update_parking_spot_status(spot["spot_number"], bool(value))
                                    logger.info(f"Updated parking spot {spot['spot_number']} status to {bool(value)}")
        
        # 创建事件循环并广播消息
        try:
            loop = asyncio.get_event_loop()
        except RuntimeError:
            # 如果没有事件循环，创建一个新的
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            
        loop.run_until_complete(broadcast_message(message_data))
        logger.info(f"Message broadcasted to {len(manager.active_connections)} WebSocket clients")
        
    except Exception as e:
        logger.error(f"Error processing message: {e}")

# Connect to MQTT broker
def connect_mqtt():
    credentials = generate_mqtt_credentials()
    # Fix: Correctly initialize the client with callback_api_version
    client = mqtt_client.Client(client_id=credentials["client_id"], callback_api_version=mqtt_client.CallbackAPIVersion.VERSION1)
    client.username_pw_set(credentials["username"], credentials["password"])
    client.on_connect = on_connect
    client.on_message = on_message
    
    try:
        logger.info(f"Connecting to MQTT broker at {MQTT_HOST}:{MQTT_PORT}")
        client.connect(MQTT_HOST, MQTT_PORT)
        return client
    except Exception as e:
        logger.error(f"Failed to connect to MQTT broker: {e}")
        return None

# Start MQTT client
mqtt_client = connect_mqtt()
if mqtt_client:
    mqtt_client.loop_start()
    logger.info("MQTT client loop started")
else:
    logger.warning("Failed to start MQTT client")

# API Endpoints
@app.get("/")
def read_root():
    return {
        "message": "Parking System API is running",
        "mqtt_connected": mqtt_client is not None and mqtt_client.is_connected(),
        "timestamp": datetime.now().isoformat()
    }

@app.get("/license-plates", response_model=Dict[str, LicensePlateData])
def get_all_license_plates():
    # Get from database instead of in-memory
    return LicensePlateRepository.get_license_plates()

@app.get("/license-plates/{plate_id}", response_model=LicensePlateData)
def get_license_plate(plate_id: str):
    plates = LicensePlateRepository.get_license_plates()
    if plate_id not in plates:
        raise HTTPException(status_code=404, detail="License plate not found")
    return plates[plate_id]

@app.get("/sensors", response_model=Dict[str, bool])
def get_all_sensors():
    # Get from database instead of in-memory
    return SensorRepository.get_sensors()

@app.get("/sensors/{sensor_id}")
def get_sensor(sensor_id: str):
    sensors = SensorRepository.get_sensors()
    if sensor_id not in sensors:
        raise HTTPException(status_code=404, detail="Sensor not found")
    return {"id": sensor_id, "value": sensors[sensor_id]}

@app.get("/debug/messages")
def get_raw_messages(limit: int = Query(10, ge=1, le=100)):
    """Get the most recent raw MQTT messages for debugging"""
    return MQTTRepository.get_mqtt_messages(limit)

@app.get("/debug/mqtt-status")
def get_mqtt_status():
    """Get the current status of the MQTT connection"""
    if not mqtt_client:
        return {"status": "not_initialized"}
    
    return {
        "status": "connected" if mqtt_client.is_connected() else "disconnected",
        "client_id": mqtt_client._client_id.decode() if hasattr(mqtt_client, "_client_id") else None,
        "subscribed_topic": TOPIC
    }

@app.post("/debug/reconnect-mqtt")
def reconnect_mqtt():
    """Force reconnect to the MQTT broker"""
    global mqtt_client
    
    if mqtt_client:
        try:
            mqtt_client.disconnect()
            mqtt_client.loop_stop()
        except:
            pass
    
    mqtt_client = connect_mqtt()
    if mqtt_client:
        mqtt_client.loop_start()
        return {"status": "reconnected", "connected": mqtt_client.is_connected()}
    else:
        raise HTTPException(status_code=500, detail="Failed to reconnect to MQTT broker")

# WebSocket endpoint
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    logger.info(f"WebSocket client connected, total connections: {len(manager.active_connections)}")
    
    try:
        # 发送连接成功消息
        await websocket.send_json({"status": "connected", "timestamp": int(time.time() * 1000)})
        
        # 发送最近的消息历史
        recent_messages = MQTTRepository.get_mqtt_messages(5)
        if recent_messages:
            await websocket.send_json({"type": "history", "messages": recent_messages})
            
        while True:
            # 保持连接活跃，接收客户端消息
            data = await websocket.receive_text()
            if data == "ping":
                await websocket.send_json({"type": "pong", "timestamp": int(time.time() * 1000)})
    except WebSocketDisconnect:
        manager.disconnect(websocket)
        logger.info(f"WebSocket client disconnected, remaining connections: {len(manager.active_connections)}")
    except Exception as e:
        logger.error(f"WebSocket error: {e}")
        manager.disconnect(websocket)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) 